package com.zty.service.impl;

import com.zty.BO.ServerInfo;
import com.zty.DTO.ServerInfoDTO;
import com.zty.service.DiscoveryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @Author ZTY
 * @Description
 * @Date 10:46 2023-12-23
 **/

@Slf4j
@Service
public class DefaultDiscoveryServiceImpl implements DiscoveryService {

    private final Map<String, List<ServerInfo>> SERVER_MAP = new ConcurrentHashMap<>();

    @Override
    public void register(ServerInfoDTO serverInfoDTO) {
        //  无需做持久化操作

        //  判断map是否存在这个server信息
        List<ServerInfo> serverInfos = SERVER_MAP.getOrDefault(serverInfoDTO.getServiceId(),new ArrayList<>());

        ServerInfo serverInfo = new ServerInfo();
        BeanUtils.copyProperties(serverInfoDTO,serverInfo);

        serverInfo.setAliveStatus(true);

        //  TODO    目前info.host不带有http/https的协议前缀

        serverInfo.setPreTimestamp(System.currentTimeMillis());

        //如果不存在，则直接放入服务对应的服务器列表
        if (!serverInfos.contains(serverInfo)){
            serverInfos.add(serverInfo);
        }

        SERVER_MAP.put(serverInfo.getServiceId(),serverInfos);

    }

    @Override
    public void heartbeat(ServerInfoDTO serverInfoDTO) {
        //  先判断服务器信息是否已注册过
        List<ServerInfo> serverInfoList = SERVER_MAP.getOrDefault(serverInfoDTO.getServiceId(),new ArrayList<>());

        //  判断服务器是否存在的标记
        boolean exists = false;

        for (ServerInfo serverInfo : serverInfoList) {
            if (serverInfo.getHost().equals(serverInfoDTO.getHost()) &&
                    serverInfo.getPort().equals(serverInfoDTO.getPort())){

                //  ip地址和端口号都一直，表示已注册过
                exists = true;
                serverInfo.setAliveStatus(true);
                serverInfo.setPreTimestamp(System.currentTimeMillis());
            }
        }

        //  如果服务器不存在，就要做一下注册服务
        if (!exists){
            register(serverInfoDTO);
        }

        SERVER_MAP.put(serverInfoDTO.getServiceId(),serverInfoList);
    }


    //  设置一个定时任务方法，每十秒检查一次服务是否存活
    @Scheduled(cron = "*/10 * * * * *")
    private void checkAlive(){
        SERVER_MAP.forEach((serviceId,serverList)->{
            serverList = serverList.stream().filter(serverInfo -> {
                //  检查每一个服务器是否存活

                //  取出该服务器上一次给Meta发送心跳的时间（秒级别）
                Long preTimestamp = serverInfo.getPreTimestamp()/1000;

                //  获取系统当前的时间（秒级别）
                long currentTimeMillis = System.currentTimeMillis()/1000;

                //  超过30秒没有发送心跳，先不移除，先做一个标记，表示怀疑
                if (currentTimeMillis - preTimestamp > 30){
                    serverInfo.setAliveStatus(false);
                }

                //  超过60秒没有发送心跳，判断服务器已经挂掉，将其移除
                return currentTimeMillis - preTimestamp < 60;
            }).collect(Collectors.toList());

            //  更新MAP
            SERVER_MAP.put(serviceId,serverList);
        });
        log.info("一轮巡检结束......");
    }

    @Override
    public Map<String, List<ServerInfo>> services() {
        return SERVER_MAP;
    }

    @Override
    public List<ServerInfo> aliveServers() {

        List<ServerInfo> chunkServers = SERVER_MAP.getOrDefault("chunk-server", new ArrayList<>());
        //  筛出所有的还在存活的分片冗余存储的服务器的信息
        return chunkServers.stream().filter(ServerInfo::isAliveStatus).collect(Collectors.toList());
    }
}
